package cn.shujuhai.common.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.shujuhai.cms.service.SysChannelService;
import cn.shujuhai.common.controller.common.CaptchaController;
import cn.shujuhai.common.domain.*;
import cn.shujuhai.common.mapper.SysUserMapper;
import cn.shujuhai.common.pluging.data.ResultCode;
import cn.shujuhai.common.pluging.data.ResultVo;
import cn.shujuhai.common.pluging.file.FilesUtils;
import cn.shujuhai.common.service.*;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author NiuYaHu
 * @description 针对表【sys_user(用户表)】的数据库操作Service实现
 * @createDate 2024-09-25 16:58:56
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
        implements SysUserService {
    final SysUserMapper sysUserMapper;
    final SysUserRoleService sysUserRoleService;
    final SysMenuService sysMenuService;
    final SysPermissionService sysPermissionService;
    final SysChannelService sysChannelService;
    final SysPermissionMenuService sysPermissionMenuService;
    final CacheManager cacheManager;
    final SettingService settingService;
    final DepartmentUserService departmentUserService;
    final DepartmentService departmentService;
    final RoleDepartmentService roleDepartmentService;
    final SysRoleService sysRoleService;
    final SysRolePermissionService sysRolePermissionService;
    final HttpServletRequest request;


    @Value("${template.static.avatarUrl}")
    public String UPLOADS_PATH;
    @Value("${template.static.viewAvatarUrl}")
    public String VIEW_PATH;
    @Value("${server.servlet.context-path}")
    private String baseUrl;

    /**
     * 登录方法
     *
     * @param loginRequest 用户登录信息
     * @param session      session
     * @return 登录成功或失败的结果及响应信息
     */
    @Override
    public Long login(LoginRequest loginRequest, HttpSession session) {
        String captchaId = loginRequest.getCaptchaId();
        String userX = loginRequest.getUserX();
        boolean b = CaptchaController.verifyCaptcha(captchaId, Integer.valueOf(userX));
        if (!b) {
            return -100L;
        }
        String username = loginRequest.getUname();
        // 安全获取userIp，防止空指针异常
        String userIp = Optional.ofNullable(session.getAttribute("userIp"))
                .map(Object::toString)
                .orElse("unknown");
        // 检查是否被锁定
        if (isLocked(username) || isLocked(userIp)) {
            throw new RuntimeException("账户已被锁定，请稍后再试");
        }
        try {
            // 解密密码
            String encryptedPassword = loginRequest.getPwd();
            String decryptAESPassword = decryptAES(encryptedPassword);
            // 获取用户ID
            Long userId = getUserIdByUsername(username);
            if (userId == null) {
                int remainingAttempts = incrementLoginFailureCount(username, userIp);
                throw new RuntimeException(String.format("用户不存在，剩余尝试次数: %d", remainingAttempts));
            }
            // 校验用户
            if (!checkUser(username, decryptAESPassword)) {
                int remainingAttempts = incrementLoginFailureCount(username, userIp);
                throw new RuntimeException(String.format("用户名或密码错误，剩余尝试次数: %d", remainingAttempts));
            }
            // 检查用户状态是否为1（禁用）
            SysUser user = getById(userId);
            if (user.getStatus() == 1) {
                throw new RuntimeException("账户已被禁用,请联系管理员");
            }
            // 登录成功，清除失败次数
            clearLoginFailureCount(username, userIp);
            return userId;
        } catch (Exception e) {
            log.error("登录失败: {}");
            throw e;
        }
    }

    /**
     * 增加登录失败次数
     * 当用户登录失败时，调用此方法增加用户的登录失败次数和IP的登录失败次数
     * 如果失败次数达到一定阈值，则锁定用户和IP
     *
     * @param username 用户名
     * @param ip       用户IP地址
     */
    private int incrementLoginFailureCount(String username, String ip) {
        Cache cache = cacheManager.getCache("loginFailureCache");
        if (cache == null) {
            throw new IllegalStateException("Cache 'loginFailureCache' not found");
        }
        // 获取当前失败次数
        Integer usernameCount = getCacheValue(cache, username);
        Integer ipCount = getCacheValue(cache, ip);
        // 增加失败次数
        cache.put(username, usernameCount + 1);
        cache.put(ip, ipCount + 1);
        // 如果失败次数达到5次，锁定用户和IP
        if (usernameCount >= 4 || ipCount >= 4) {
            lockUserOrIp(username, ip);
        }
        // 返回剩余的登录尝试次数
        return Math.max(0, 5 - (usernameCount + 1));
    }

    /**
     * 从缓存中获取指定键的值
     * 如果键不存在，则返回0
     *
     * @param cache 缓存对象
     * @param key   键
     * @return 键对应的值，如果不存在则返回0
     */
    private Integer getCacheValue(Cache cache, String key) {
        Cache.ValueWrapper valueWrapper = cache.get(key);
        return valueWrapper != null ? (Integer) valueWrapper.get() : Integer.valueOf(0);
    }

    /**
     * 清除登录失败次数
     * 当用户登录成功时，调用此方法清除用户的登录失败次数和IP的登录失败次数
     *
     * @param username 用户名
     * @param ip       用户IP地址
     */
    private void clearLoginFailureCount(String username, String ip) {
        Cache cache = cacheManager.getCache("loginFailureCache");
        if (cache == null) {
            throw new IllegalStateException("Cache 'loginFailureCache' not found");
        }
        cache.evict(username);
        cache.evict(ip);
    }

    /**
     * 锁定用户或IP
     * 当用户登录失败次数达到一定阈值时，调用此方法锁定用户和IP
     *
     * @param username 用户名
     * @param ip       用户IP地址
     */
    private void lockUserOrIp(String username, String ip) {
        Cache cache = cacheManager.getCache("lockCache");
        if (cache == null) {
            throw new IllegalStateException("Cache 'lockCache' not found");
        }
        // 使用自定义的过期时间处理逻辑
        long expirationTimeMillis = System.currentTimeMillis() + 30 * 60 * 1000; // 30分钟
        // 锁定用户和IP
        cache.put(username, new Date(expirationTimeMillis));
        cache.put(ip, new Date(expirationTimeMillis));
    }

    /**
     * 检查是否过期
     * 比较缓存中的日期和当前日期，判断是否过期
     *
     * @param cachedDate 缓存中的日期
     * @return 如果过期返回true，否则返回false
     */
    private boolean isExpired(Date cachedDate) {
        return cachedDate != null && cachedDate.getTime() < System.currentTimeMillis();
    }

    /**
     * 检查是否被锁定
     * 根据键检查对象是否被锁定，如果锁定时间未过期，则返回true
     *
     * @param key 键，可以是用户名或IP地址
     * @return 如果被锁定返回true，否则返回false
     */
    private boolean isLocked(String key) {
        Cache cache = cacheManager.getCache("lockCache");
        if (cache == null) {
            throw new IllegalStateException("Cache 'lockCache' not found");
        }
        Cache.ValueWrapper valueWrapper = cache.get(key);
        if (valueWrapper != null) {
            Date cachedDate = (Date) valueWrapper.get();
            return !isExpired(cachedDate);
        }
        return false;
    }

    /**
     * 检测用户密码是否过期
     *
     * @param userId 用户id
     * @return 密码是否过期的结果
     */
    public boolean isPasswordExpired(Long userId) {
        SysUser user = this.getById(userId);
        if (user == null || user.getLastPasswordChangeDate() == null) {
            return true;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(user.getLastPasswordChangeDate());
        calendar.add(Calendar.DAY_OF_MONTH, 30);
        return new Date().after(calendar.getTime());
    }

    /**
     * 检查用户密码是否过期并更新提示次数
     *
     * @param userId 用户ID
     * @return 返回密码过期的可能次数
     */
    public int checkAndHandlePasswordExpiration(Long userId) {
        SysUser user = this.getById(userId);
        if (user == null || user.getLastPasswordChangeDate() == null) {
            return -1; // 用户不存在或无上次修改密码日期
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(user.getLastPasswordChangeDate());
        calendar.add(Calendar.DAY_OF_MONTH, 30);
        if (new Date().after(calendar.getTime())) {
            int promptCount = user.getPasswordExpirePromptCount() != null ? user.getPasswordExpirePromptCount() : 1;
            if (promptCount >= 3) {
                user.setStatus(1); // 禁用用户
            } else {
                user.setPasswordExpirePromptCount(promptCount + 1);
            }
            this.updateById(user);
            return promptCount;
        }
        return 0; // 密码未过期
    }


    /**
     * 解密密码
     *
     * @param encryptedData encryptedData
     */
    private String decryptAES(String encryptedData) {
        try {
            String secretKey = settingService.getSecretKey();
            String initVector = settingService.getInitVector();

            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            byte[] original = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
            return new String(original, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("解密失败", e);
        }
    }

    /**
     * 通过用户名获取用户密码
     *
     * @param username 用户名
     * @return 如果用户名为null, 返回空，否则，返回密码
     */
    public String getPasswordByUsername(String username) {
        SysUser user = this.getOne(new QueryWrapper<SysUser>().select("password").eq("username", username));
        return user == null ? null : user.getPassword();
    }

    /**
     * 检测用户账号密码
     *
     * @param username 用户名
     * @param password 密码
     * @return 如果账号、密码正确，返回true，否则返回false
     */
    @Override
    public boolean checkUser(String username, String password) {
        // 获取数据库中存储的加密密码
        String storedPassword = getPasswordByUsername(username);
        if (storedPassword == null) {
            return false;
        }
        // 使用MD5加密用户输入的密码，并与数据库中的加密密码进行比较
        return SaSecureUtil.md5(password).equals(storedPassword);
    }

    /**
     * 根据用户名查询用户id
     *
     * @param username 用户名
     * @return 返回当前用户名的用户Id
     */
    @Override
    public Long getUserIdByUsername(String username) {
        SysUser user = this.getOne(new QueryWrapper<SysUser>().eq("username", username));
        return user == null ? null : user.getUserId();
    }

    /**
     * 根据用户ID查询系统用户信息
     *
     * @param userId 用户ID
     * @return 返回对应的系统用户信息对象（SysUser），如果未找到则返回null
     */
    @Override
    public SysUser getUserById(Long userId) {
        return this.getById(userId);
    }

    /**
     * 获取用户权限列表
     *
     * @param userId 用户Id
     * @return 用户权限列表
     */
    @Override
    public List<String> getPermissionList(Long userId) {
        // 从 Session 中获取部门ID
        HttpSession session = request.getSession();
        Long departmentId = (Long) session.getAttribute("departmentId");
        List<String> allPermissions = new ArrayList<>();

        // 1. 查询用户自身权限
        List<String> userPermissions = this.sysUserMapper.selectPermissionCodesByUserId(userId);
        if (userPermissions != null && !userPermissions.isEmpty()) {
            allPermissions.addAll(userPermissions);
        }
        // 2. 查询部门角色权限（如果 departmentId 不为空）
        if (departmentId != null) {
            // 获取用户在部门中的角色ID列表
            List<Long> roleIds = this.roleDepartmentService.list(
                    new QueryWrapper<RoleDepartment>()
                            .lambda()
                            .eq(RoleDepartment::getDepartmentId, departmentId)
                            .eq(RoleDepartment::getUserId, userId)
            ).stream().map(RoleDepartment::getRoleId).toList();
            if (!roleIds.isEmpty()) {
                // 根据角色ID查询权限ID
                List<Long> permissionIds = this.sysRolePermissionService.list(
                        new QueryWrapper<SysRolePermission>()
                                .lambda()
                                .in(SysRolePermission::getRoleId, roleIds)
                ).stream().map(SysRolePermission::getPermissionId).toList();
                if (!permissionIds.isEmpty()) {
                    // 根据权限ID查询权限代码
                    List<String> permissionCodes = this.sysPermissionService.list(
                            new QueryWrapper<SysPermission>()
                                    .lambda()
                                    .in(SysPermission::getPermissionId, permissionIds)
                    ).stream().map(SysPermission::getPermissionCode).toList();
                    if (!permissionCodes.isEmpty()) {
                        allPermissions.addAll(permissionCodes);
                    }
                }
            }
        }

        // 去重并返回
        List<String> result = allPermissions.stream().distinct().toList();
        return result;
    }

    /**
     * 获取用户角色列表
     *
     * @param userId 用户Id
     * @return 用户角色列表
     */
    @Override
    public List<String> getRoleList(Long userId) {
        // 从 Session 中获取部门ID
        HttpSession session = request.getSession();
        Long departmentId = (Long) session.getAttribute("departmentId");
        List<String> allRoles = new ArrayList<>();
        // 1. 查询用户自身角色
        List<String> userRoles = sysUserMapper.selectRoleCodesByUserId(userId);
        if (userRoles != null && !userRoles.isEmpty()) {
            allRoles.addAll(userRoles);
        }
        // 2. 查询部门角色（如果 departmentId 不为空）
        if (departmentId != null) {
            List<RoleDepartment> roleDepartmentList = roleDepartmentService.list(
                    new QueryWrapper<RoleDepartment>()
                            .lambda()
                            .eq(RoleDepartment::getDepartmentId, departmentId)
                            .eq(RoleDepartment::getUserId, userId)
            );
            List<Long> roleIds = roleDepartmentList.stream()
                    .map(RoleDepartment::getRoleId)
                    .toList();
            if (!roleIds.isEmpty()) {
                List<String> deptRoleCodes = sysRoleService.list(
                                new QueryWrapper<SysRole>()
                                        .lambda()
                                        .in(SysRole::getRoleId, roleIds)
                        ).stream()
                        .map(SysRole::getRoleCode)
                        .toList();
                if (!deptRoleCodes.isEmpty()) {
                    allRoles.addAll(deptRoleCodes);
                }
            }
        }
        // 去重并返回
        List<String> result = allRoles.stream().distinct().toList();
        return result;
    }

    /**
     * 根据用户ID查询系统用户信息
     *
     * @param userId 用户ID
     * @return 返回对应的系统用户信息对象（SysUser），如果未找到则返回null
     */
    @Override
    public SysUser selectById(Integer userId) {
        return this.getById(userId);
    }

    /**
     * 分页查询系统用户信息
     *
     * @param username 用户名（可为null，不为null时进行模糊查询）
     * @param name     真实姓名（可为null，不为null时进行模糊查询）
     * @param page     当前页码（从1开始）
     * @param pageSize 每页显示的记录数
     * @return 返回包含分页信息和系统用户列表的ResultVo对象，其中PageInfo<SysUser>包含了分页的详细信息，如总记录数、总页数等
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<PageInfo<SysUser>> getSysUserPageInfo(String username, String name, Integer page, Integer pageSize, Long departmentId) {
        // 获取当前登录用户的角色代码
        List<String> roleCodes = this.sysUserMapper.selectRoleCodesByUserId(StpUtil.getLoginIdAsLong());
        // 分页查询
        PageHelper.startPage(page, pageSize);
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        // 如果当前用户不是 super_admin，则只显示前端传过来的部门下的用户
        if (!roleCodes.contains("super_admin")) {
            if (departmentId != null) {
                // 查询该部门下的所有用户ID
                List<Long> userIdsInDept = this.departmentUserService.list(
                                new QueryWrapper<DepartmentUser>().lambda()
                                        .eq(DepartmentUser::getDepartmentId, departmentId)
                                        .select(DepartmentUser::getUserId)
                        ).stream()
                        .map(DepartmentUser::getUserId)
                        .collect(Collectors.toList());
                if (!userIdsInDept.isEmpty()) {
                    queryWrapper.lambda().in(SysUser::getUserId, userIdsInDept);
                } else {
                    // 如果该部门下没有用户，直接返回空结果
                    PageInfo<SysUser> emptyPageInfo = new PageInfo<>();
                    emptyPageInfo.setList(Collections.emptyList());
                    return ResultVo.success(emptyPageInfo);
                }
            } else {
                // 如果前端没有传 departmentId，可以根据业务需求决定：
                // 1. 返回空列表；2. 返回当前用户所在部门用户；3. 返回全部用户
                // 这里推荐返回空列表，避免数据泄露
                PageInfo<SysUser> emptyPageInfo = new PageInfo<>();
                emptyPageInfo.setList(Collections.emptyList());
                return ResultVo.success(emptyPageInfo);
            }
        }
        // 添加用户名和姓名的模糊查询条件
        if (username != null && !username.isEmpty()) {
            queryWrapper.like("username", username);
        }
        if (name != null && !name.isEmpty()) {
            queryWrapper.like("name", name);
        }
        // 查询用户列表并排序
        List<SysUser> sysUserList = this.list(queryWrapper.lambda().orderByDesc(SysUser::getUserId));
        // 查询部门信息并整合
        for (SysUser user : sysUserList) {
            // 1. 通过中间表 DepartmentUser 查询所有 departmentId
            List<DepartmentUser> departmentUserList = this.departmentUserService.list(
                    new QueryWrapper<DepartmentUser>().eq("user_id", user.getUserId())
            );
            // 2. 通过 departmentId 查询 Department 表获取部门名称
            List<Department> departmentList = new ArrayList<>();
            for (DepartmentUser departmentUser : departmentUserList) {
                Department department = this.departmentService.getOne(
                        new QueryWrapper<Department>().lambda().eq(Department::getDepartmentId, departmentUser.getDepartmentId())
                );
                if (!ObjectUtil.isEmpty(department)) {
                    departmentList.add(department);
                }
            }
            // 3. 设置部门信息到 SysUser 对象
            user.setDepartmentList(departmentList); // 假设 SysUser 类中有一个 List<Department> 类型的字段 departments
        }
        PageInfo<SysUser> sysUserPageInfo = new PageInfo<>(sysUserList);
        return ResultVo.success(sysUserPageInfo);
    }

    /**
     * 启用｜禁用 用户
     *
     * @param userId 用户id
     * @return 获取修改id, 返回更新后的用户
     */
    @Override
    public ResultVo<SysUser> enableUser(Integer userId) {
        // 检查用户是否存在
        SysUser sysUser = this.selectById(userId);
        if (sysUser == null) {
            return ResultVo.failure(ResultCode.USER_NOT_EXIST);
        }
        // 切换状态
        sysUser.setStatus(sysUser.getStatus() == 0 ? 1 : 0);

        // 更新用户状态
        if (this.updateById(sysUser)) {
            return ResultVo.success();
        }
        return ResultVo.failure(ResultCode.FAILED);
    }

    /**
     * 上传用户头像
     *
     * @param file 上传的文件
     * @return url
     * @Param avatar 用户头像
     */

    @Override
    public ResultVo<?> uploadAvatar1(MultipartFile file) throws IOException {
        Cache cache = cacheManager.getCache("sys_setting");
        if (cache == null) {
            throw new IllegalStateException("未获取到缓存");
        }
        String s = cache.get("AVATAR_SIZE", String.class);
        s = StrUtil.trim(s);
        int avatarSize = StrUtil.isNotEmpty(s) ? Integer.parseInt(s) : 2048;
        if (file.getSize() > avatarSize * 1024L) {
            return ResultVo.failure("用户上传的头像大小不能超过" + avatarSize + "kb");
        }
        return ResultVo.success(FilesUtils.uploadFile(file, UPLOADS_PATH, VIEW_PATH));
    }

    /**
     * 用户新增
     *
     * @param sysUser 新增用户对象
     * @return 新增用户对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<Void> add(@Validated(SysUser.CreateGroup.class) SysUser sysUser) {
        Cache cache = cacheManager.getCache("sys_setting");
        //判断用户是否被占用
        QueryWrapper<SysUser> query = new QueryWrapper<>();
        query.lambda().eq(SysUser::getUsername, sysUser.getUsername());
        if (this.getOne(query) != null) {
            return ResultVo.failure(ResultCode.USER_EXIST);
        }
        // 验证邮箱是否已存在
        query.clear();  // 清除之前的条件
        query.lambda().eq(SysUser::getEmail, sysUser.getEmail());
        if (this.getOne(query) != null) {
            return ResultVo.failure(ResultCode.EMAIL_EXIST);
        }
        // 验证电话是否已存在
        query.clear();  // 清除之前的条件
        query.lambda().eq(SysUser::getPhone, sysUser.getPhone());
        if (this.getOne(query) != null) {
            return ResultVo.failure(ResultCode.PHONE_EXIST);
        }
        String password = decryptAES(sysUser.getPassword());
        //密码加密
        sysUser.setPassword(DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)));
        //设置默认头像
        if (sysUser.getAvatar().isEmpty()) {
            assert cache != null;
            sysUser.setAvatar(cache.get("DEFAULT_AVATAR", String.class));
        }
        // 设置密码过期时间为一个月以前
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MONTH, 1);
        sysUser.setLastPasswordChangeDate(calendar.getTime());
        if (!this.save(sysUser)) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        if (sysUser.getDepartmentIds() != null && !sysUser.getDepartmentIds().isEmpty()) {
            // 创建一个集合用于存储 DepartmentUser 对象
            List<DepartmentUser> departmentUserList = new ArrayList<>();
// 遍历 sysUser 的 departmentIds 集合
            for (Long departmentId : sysUser.getDepartmentIds()) {
                DepartmentUser departmentUser = new DepartmentUser();
                departmentUser.setUserId(sysUser.getUserId());
                departmentUser.setDepartmentId(departmentId);
                // 将对象添加到集合中
                departmentUserList.add(departmentUser);
            }
// 调用批量保存方法
            if (!this.departmentUserService.saveBatch(departmentUserList)) {
                // 如果批量保存失败，返回失败结果
                return ResultVo.failure(ResultCode.FAILED);
            }
        }
        return ResultVo.success();
    }

    /**
     * 用户编辑
     *
     * @param sysUser 编辑用户对象
     * @return 更新用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<SysUser> edit(@Validated(SysUser.UpdateGroup.class) SysUser sysUser) {
        QueryWrapper<SysUser> query = new QueryWrapper<>();
        query.lambda().eq(SysUser::getEmail, sysUser.getEmail()).ne(SysUser::getUserId, sysUser.getUserId());
        if (this.getOne(query) != null) {
            return ResultVo.failure(ResultCode.EMAIL_EXIST);
        }
        // 验证电话是否已存在
        query.clear();  // 清除之前的条件
        query.lambda().eq(SysUser::getPhone, sysUser.getPhone()).ne(SysUser::getUserId, sysUser.getUserId());
        if (this.getOne(query) != null) {
            return ResultVo.failure(ResultCode.PHONE_EXIST);
        }
        sysUser.setUsername(null);
        if (!this.updateById(sysUser)) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        List<Long> departmentIds = this.departmentUserService.list(new QueryWrapper<DepartmentUser>().lambda().eq(DepartmentUser::getUserId, sysUser.getUserId()))
                .stream()
                .map(DepartmentUser::getDepartmentId)
                .toList();
        if (sysUser.getDepartmentIds() != null) {
            if (!sysUser.getDepartmentIds().equals(departmentIds)) {
                // 创建一个集合用于存储 DepartmentUser 对象
                List<DepartmentUser> departmentUserList = new ArrayList<>();
// 如果 departmentUser 为空，创建一个新的对象
                this.departmentUserService.remove(new QueryWrapper<DepartmentUser>().eq("user_id", sysUser.getUserId()));
                // 遍历 sysUser 的 departmentIds 集合
                for (Long departmentId : sysUser.getDepartmentIds()) {
                    DepartmentUser departmentUser = new DepartmentUser();
                    departmentUser.setUserId(sysUser.getUserId());
                    departmentUser.setDepartmentId(departmentId);
                    // 将对象添加到集合中
                    departmentUserList.add(departmentUser);
                }
                if (!this.departmentUserService.saveBatch(departmentUserList)) {
                    // 如果批量保存失败，返回失败结果
                    return ResultVo.failure(ResultCode.FAILED);
                }
            }
        }
        return ResultVo.success();
    }

    /**
     * 根据用户id获取对应角色id
     *
     * @return 角色id集合
     */
    @Override
    public ResultVo<?> getRoleIdsByUserId(Long userId) {
        List<Long> list = this.sysUserRoleService.list(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, userId))
                .stream()
                .map(SysUserRole::getRoleId)
                .collect(Collectors.toList());

        return ResultVo.success(list);
    }

    /**
     * 根据用户id删除对应用户
     *
     * @param userId 用户ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<SysUser> removeId(Integer userId) {
        // 检查用户是否为当前登录用户
        if (userId.equals(getCurrentCreateUser())) {
            return ResultVo.failure(ResultCode.SELF_DELETE_FORBIDDEN);
        }
        SysUser sysUser = this.getOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUserId, userId));
        if (sysUser.getStatus() == 0) {
            return ResultVo.failure(ResultCode.ALREADY_ENABLE);
        }
        List<SysUserRole> sysUserRole = this.sysUserRoleService.list(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, userId));
        if (!sysUserRole.isEmpty()) {
            this.sysUserRoleService.remove(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, userId));

        }
        this.departmentUserService.remove(new QueryWrapper<DepartmentUser>().lambda().eq(DepartmentUser::getUserId, userId));
        if (!this.removeById(userId)) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        return ResultVo.success();
    }

    /**
     * 获取当前登录用户Id
     *
     * @return 当前登录用户Id
     */
    private Integer getCurrentCreateUser() {
        return StpUtil.getLoginIdAsInt();
    }

    /**
     * 重置密码
     *
     * @param userId 用户id
     * @return 返回重置密码成功或失败的结果
     */
    @Override
    public ResultVo<?> userResetPassword(Long userId) {
        Cache cache = cacheManager.getCache("sys_setting");
        SysUser sysUser = this.getById(userId);
        if (sysUser == null) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        //加密
        assert cache != null;
        sysUser.setPassword(DigestUtils.md5DigestAsHex(Objects.requireNonNull(cache.get("RESET_PWD", String.class)).getBytes(StandardCharsets.UTF_8)));
        sysUser.setPasswordExpirePromptCount(1);
        if (!this.updateById(sysUser)) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        return ResultVo.success(sysUser);
    }

    /**
     * 修改密码
     *
     * @param oldPwd 原密码
     * @param newPwd 新密码
     * @return 修改密码成功或失败的结果及响应信息
     */
    @Override
    public ResultVo<?> updatePassword(String oldPwd, String newPwd) {
        SysUser user = this.getUserById(StpUtil.getLoginIdAsLong());
        String old = decryptAES(oldPwd);
        String newP = decryptAES(newPwd);
        oldPwd = DigestUtils.md5DigestAsHex(old.getBytes(StandardCharsets.UTF_8)); //旧密码加密
        newPwd = DigestUtils.md5DigestAsHex(newP.getBytes(StandardCharsets.UTF_8)); //新密码加密
        // 校验旧密码是否正确
        if (!oldPwd.equals(user.getPassword())) {
            return ResultVo.failure(ResultCode.OLD_NO_SAME);
        }

        // 校验新密码是否为空
        if (newPwd.isEmpty()) {
            return ResultVo.failure(ResultCode.NEW_PASSWORD_EMPTY);
        }

        // 校验新密码是否与旧密码相同
        if (newPwd.equals(oldPwd)) {
            return ResultVo.failure(ResultCode.NEW_OLD_SAME);
        }

        // 更新用户密码
        user.setPassword(newPwd);
        user.setLastPasswordChangeDate(new Date());
        user.setPasswordExpirePromptCount(1);

        // 保存用户信息
        if (this.updateById(user)) {
            // 退出当前登录
            StpUtil.logout();
            return ResultVo.success();
        } else {
            return ResultVo.failure(ResultCode.FAILED);
        }
    }

    /**
     * 获取用户菜单列表
     *
     * @return 用户所对应的菜单集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<SysMenu> getUserMenuByPerm(HttpServletRequest request) {
        HttpSession session = request.getSession();
        Long departmentId = (Long) session.getAttribute("departmentId");
        long loginIdAsLong = StpUtil.getLoginIdAsLong();
        List<Long> channelIds = this.sysUserMapper.selectChannelIdByUserId(loginIdAsLong); //根据用户Id获取栏目id集合
        List<Long> infoIds = this.sysUserMapper.selectInfoIdByUserId(loginIdAsLong); //根据用户Id获取信息Id集合
        List<Long> userMenuIds = this.sysUserMapper.getUserMenuIds(loginIdAsLong);   //根据用户Id获取对应基础菜单Id集合
        List<Long> id = this.sysUserMapper.getUserMenuIdsByUserIdAndDeptId(loginIdAsLong, departmentId);
        userMenuIds.addAll(id);
        // 使用Stream API去重
        List<Long> uniqueMenuIdList = userMenuIds.stream()
                .distinct()
                .collect(Collectors.toList());
        // 获取特定权限的菜单ID
        Long channelMenuId = this.getMenuIdByPermissionCode("channel_admin");
        Long infoMenuId = this.getMenuIdByPermissionCode("info_admin");

        // 获取基础菜单
        List<SysMenu> sysMenuList = this.sysMenuService.getList(uniqueMenuIdList);
        setMenuIdToId(sysMenuList);   //将map集合转换为list集合
        List<SysMenu> finalMenuList = new ArrayList<>();
        processMenuList(sysMenuList, finalMenuList, infoMenuId, channelMenuId, channelIds, infoIds);
        // 返回最终的菜单列表
        return finalMenuList;
    }

    public void processMenuList(List<SysMenu> sysMenuList, List<SysMenu> finalMenuList, Long infoMenuId, Long channelMenuId, List<Long> channelIds, List<Long> infoIds) {
        for (SysMenu menu : sysMenuList) {
            // 如果当前菜单不是目标菜单之一，则直接加入最终菜单列表
            if (!menu.getMenuId().equals(infoMenuId) && !menu.getMenuId().equals(channelMenuId)) {
                finalMenuList.add(menu);
            } else {
                // 如果是 channelMenuId 相关菜单
                if (menu.getMenuId().equals(channelMenuId)) {
                    if (!channelIds.isEmpty()) {
                        // 使用修改后的 channel 菜单
                        SysMenu modifiedMenu = this.getModifiedMenu(channelIds, menu, "channel");
                        finalMenuList.add(modifiedMenu);
                    } else {
                        finalMenuList.add(menu);
                    }
                }
                // 如果是 infoMenuId 相关菜单
                if (menu.getMenuId().equals(infoMenuId)) {
                    if (!infoIds.isEmpty()) {
                        // 使用修改后的 info 菜单
                        SysMenu modifiedMenu = this.getModifiedMenu(infoIds, menu, "info");
                        finalMenuList.add(modifiedMenu);
                    } else {
                        finalMenuList.add(menu);
                    }
                }
            }

            // 如果当前菜单有子菜单，则递归处理子菜单
            if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                if (menu.getChildren().getFirst().getMenuId() == null) {
                    continue;
                }
                List<SysMenu> newChildren = new ArrayList<>();
                // 递归调用处理子菜单
                processMenuList(menu.getChildren(), newChildren, infoMenuId, channelMenuId, channelIds, infoIds);
                menu.setChildren(newChildren); // 重新设置子菜单
            }
        }
    }


    /**
     * 根据登录用户id获取用户基本信息
     *
     * @return 获取到的登录用户信息
     */
    @Override
    public ResultVo<SysUser> getUserByLoginId() {
        return ResultVo.success(this.selectById(this.getCurrentCreateUser()));
    }

    /**
     * 更新登录用户头像
     *
     * @param avatar 用户头像路径
     * @return 获取到的登录用户信息
     */
    @Override
    public ResultVo<SysUser> updateLoginAvatar(String avatar) {
        SysUser sysUser = this.getById(this.getCurrentCreateUser());
        sysUser.setAvatar(avatar);
        this.updateById(sysUser);
        return ResultVo.success(sysUser);

    }

    /**
     * 根据权限代码获取对应菜单Id
     *
     * @param permissionCode 权限代码
     * @return 菜单Id
     */
    private Long getMenuIdByPermissionCode(String permissionCode) {
        // 根据权限代码查询权限实体
        QueryWrapper<SysPermission> permissionQueryWrapper = new QueryWrapper<>();
        permissionQueryWrapper.lambda().eq(SysPermission::getPermissionCode, permissionCode);
        SysPermission sysPermission = this.sysPermissionService.getOne(permissionQueryWrapper);

        if (sysPermission != null) {
            // 根据权限ID查询权限菜单中间表，获取菜单ID
            QueryWrapper<SysPermissionMenu> permissionMenuQueryWrapper = new QueryWrapper<>();
            permissionMenuQueryWrapper.lambda().eq(SysPermissionMenu::getPermissionId, sysPermission.getPermissionId());
            SysPermissionMenu sysPermissionMenu = this.sysPermissionMenuService.list(permissionMenuQueryWrapper).getFirst();

            return sysPermissionMenu != null ? sysPermissionMenu.getMenuId() : null;
        }
        return null;
    }

    /**
     * 为基础菜单添加子菜单
     *
     * @param ids          添加的子菜单集合
     * @param originalMenu 构建的菜单树
     * @param module       对应的菜单模块
     * @return 构建的子菜单树
     */
    private SysMenu getModifiedMenu(List<Long> ids, SysMenu originalMenu, String module) {
        if (originalMenu != null) {
            List<Map<String, Object>> maps = this.sysChannelService.buildChannelMenuTree(ids, originalMenu.getHref(), module);
            List<SysMenu> sysMenus = convertMapsToSysMenus(maps);
            originalMenu.setChildren(sysMenus);
        }
        return originalMenu;
    }

    /**
     * 将map集合转换为list集合
     *
     * @param maps 传入的map集合
     * @return 转换后的list集合
     */
    @SuppressWarnings("unchecked")
    public List<SysMenu> convertMapsToSysMenus(List<Map<String, Object>> maps) {
        return maps.stream()
                .map(map -> {
                    SysMenu sysMenu = new SysMenu();
                    // 假设Map中的键与SysMenu的属性名相匹配
                    sysMenu.setId((String) map.get("id"));
                    sysMenu.setTitle((String) map.get("title"));
                    sysMenu.setType((Long) map.get("type"));
                    sysMenu.setHref((String) map.get("href"));
                    sysMenu.setChildren((List<SysMenu>) map.get("children"));
                    return sysMenu;
                })
                .collect(Collectors.toList());
    }

    /**
     * 递归方法，用于遍历 sysMenuList 和所有子菜单，并将 menuId 赋值给 id 字段
     *
     * @param sysMenuList 菜单集合
     */

    public void setMenuIdToId(List<SysMenu> sysMenuList) {
        for (SysMenu menu : sysMenuList) {
            menu.setHref(baseUrl + menu.getHref());
            // 将 menuId 赋值给 id 字段
            menu.setId(String.valueOf(menu.getMenuId()));
            // 如果该菜单有子菜单，递归处理子菜单
            if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                setMenuIdToId(menu.getChildren());
            }
        }
    }

    /**
     * 通过用户名获取用户id
     *
     * @param userId 用户id
     * @return 获取的用户名结果
     */
    @Override
    public String getUsernameById(Long userId) {
        SysUser user = this.getById(userId);
        return user == null ? null : user.getUsername();
    }

    @Override
    public ResultVo<SysUser> updateUserInfo(SysUser sysUser) {
        this.updateById(sysUser);
        return ResultVo.success();
    }

    @Override
    public ResultVo<?> getUserList() {
        List<SysUser> list = this.list(Wrappers.<SysUser>lambdaQuery().select(SysUser::getUserId, SysUser::getName));
        return ResultVo.success(list);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<?> fpRole(DepartmentRole departmentRole) {
        Long userId = departmentRole.getUserId();
        // 先删除该用户原有的个人角色
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().eq("user_id", userId));
        // 处理个人角色（roleIds 不为空）
        if (departmentRole.getRoleIds() != null && !departmentRole.getRoleIds().isEmpty()) {

            // 插入新的个人角色
            List<SysUserRole> sysUserRoles = departmentRole.getRoleIds().stream()
                    .map(roleId -> {
                        SysUserRole sysUserRole = new SysUserRole();
                        sysUserRole.setUserId(userId);
                        sysUserRole.setRoleId(roleId);
                        return sysUserRole;
                    })
                    .collect(Collectors.toList());

            if (!sysUserRoleService.saveBatch(sysUserRoles)) {
                return ResultVo.failure(ResultCode.FAILED);
            }
        }
        // 处理部门角色（departmentRoleId 和 departmentId 不为空）
        if (departmentRole.getDepartmentRoleId() != null && departmentRole.getDepartmentId() != null) {
            // 先删除该用户在该部门下的原有部门角色
            roleDepartmentService.remove(
                    new QueryWrapper<RoleDepartment>()
                            .eq("user_id", userId)
                            .eq("department_id", departmentRole.getDepartmentId())
            );
            // 插入新的部门角色
            RoleDepartment roleDepartment = new RoleDepartment();
            roleDepartment.setUserId(userId);
            roleDepartment.setRoleId(departmentRole.getDepartmentRoleId());
            roleDepartment.setDepartmentId(departmentRole.getDepartmentId());

            if (!roleDepartmentService.save(roleDepartment)) {
                return ResultVo.failure(ResultCode.FAILED);
            }
        }

        return ResultVo.success();
    }

    @Override
    public ResultVo<?> getRoleByUserId(Long userId) {
        List<Long> list = this.sysUserRoleService.list(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, userId))
                .stream()
                .map(SysUserRole::getRoleId)
                .toList();
        if (list.isEmpty()) {
            return ResultVo.success(Collections.emptyList());
        }
        List<SysRole> list1 = this.sysRoleService.list(new QueryWrapper<SysRole>().lambda().in(SysRole::getRoleId, list));
        return ResultVo.success(list1);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<List<String>> getUserDepartmentRoles(Long userId) {
        List<RoleDepartment> roleDepartmentList = this.roleDepartmentService.list(
                new QueryWrapper<RoleDepartment>().eq("user_id", userId)
        );
        List<String> resultList = new ArrayList<>();
        if (roleDepartmentList == null) {
            return ResultVo.success(Collections.emptyList());
        }
        for (RoleDepartment rd : roleDepartmentList) {
            Long departmentId = rd.getDepartmentId();
            Long roleId = rd.getRoleId();

            Department department = this.departmentService.getById(departmentId);
            String departmentName = department != null ? department.getDepartmentName() : "未知部门";

            SysRole role = this.sysRoleService.getById(roleId);
            String roleName = role != null ? role.getRoleName() : "未知角色";

            resultList.add(departmentName + roleName);
        }

        return ResultVo.success(resultList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<?> importUser(MultipartFile file) throws IOException {
        /* 1. 读 Excel */
        List<SysUser> list = EasyExcel.read(file.getInputStream())
                .head(SysUser.class)
                .sheet()
                .headRowNumber(2)
                .doReadSync();
        if (CollUtil.isEmpty(list)) {
            return ResultVo.failure("文件无数据");
        }

        /* 2. 预查重：一次性把库里已有的用户名/邮箱/手机取回 */
        Set<String> existUsernames = new HashSet<>();
        Set<String> existEmails = new HashSet<>();
        Set<String> existPhones = new HashSet<>();

        List<SysUser> dbList = lambdaQuery()
                .select(SysUser::getUsername, SysUser::getEmail, SysUser::getPhone)
                .list();
        dbList.forEach(u -> {
            existUsernames.add(u.getUsername());
            if (StrUtil.isNotBlank(u.getEmail())) existEmails.add(u.getEmail());
            if (StrUtil.isNotBlank(u.getPhone())) existPhones.add(u.getPhone());
        });

        /* 3. 行级校验 & 组装 */
        List<SysUser> insertList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            SysUser u = list.get(i);
            int row = i + 3; // 表头+索引

            /* 必填 */
            if (StrUtil.isBlank(u.getUsername()))
                return ResultVo.failure("第" + row + "行：用户名不能为空");
            if (u.getName() == null)
                return ResultVo.failure("第" + row + "行：姓名不能为空");
            if (u.getAge() == null)
                return ResultVo.failure("第" + row + "行：年龄不能为空");
            if (u.getSex() == null || !List.of(1, 2).contains(u.getSex()))
                return ResultVo.failure("第" + row + "行：性别只能是 1(男) 或 2(女)");

            /* 格式 */
            if (StrUtil.isNotBlank(u.getEmail()) && !Validator.isEmail(u.getEmail()))
                return ResultVo.failure("第" + row + "行：邮箱格式错误");
            if (StrUtil.isNotBlank(u.getPhone()) && !Validator.isMobile(u.getPhone()))
                return ResultVo.failure("第" + row + "行：手机号格式错误");

            /* 唯一 */
            if (existUsernames.contains(u.getUsername()))
                return ResultVo.failure("第" + row + "行：用户名已存在");
            if (StrUtil.isNotBlank(u.getEmail()) && existEmails.contains(u.getEmail()))
                return ResultVo.failure("第" + row + "行：邮箱已存在");
            if (StrUtil.isNotBlank(u.getPhone()) && existPhones.contains(u.getPhone()))
                return ResultVo.failure("第" + row + "行：手机号已存在");

            /* 统一密码 */
            u.setPassword(DigestUtils.md5DigestAsHex("123456@Aa".getBytes(StandardCharsets.UTF_8)));
            insertList.add(u);
        }

        /* 4. 批量插入用户 */
        if (!this.saveBatch(insertList)) {
            return ResultVo.failure(ResultCode.FAILED);
        }

        HttpSession session = request.getSession();
        Long departmentId = (Long) session.getAttribute("departmentId");
        /* 6. 构造 DepartmentUser 列表 */
        List<DepartmentUser> deptUserList = insertList.stream()
                .map(u -> {
                    DepartmentUser du = new DepartmentUser();
                    du.setUserId(u.getUserId());
                    du.setDepartmentId(departmentId);
                    return du;
                })
                .collect(Collectors.toList());
        /* 7. 调用 departmentUserService 批量插入 */
        this.departmentUserService.saveBatch(deptUserList);
        return ResultVo.success(insertList.size());
    }

    @Override
    public ResultVo<?> getUserByDepartmentId(Long departmentId) {
        List<Long> userIds = this.departmentUserService.list(new QueryWrapper<DepartmentUser>().lambda().eq(DepartmentUser::getDepartmentId, departmentId))
                .stream()
                .map(DepartmentUser::getUserId)
                .toList();
        List<SysUser> list = this.list(new QueryWrapper<SysUser>().lambda().in(SysUser::getUserId, userIds));
        if (list.isEmpty()) {
            return ResultVo.success(Collections.emptyList());
        }
        return ResultVo.success(list);
    }
}




